#region Header

// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
//     <version>$Revision: 2682 $</version>
// </file>

#endregion Header

namespace ICSharpCode.TextEditor.Document
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    using ICSharpCode.SharpDevelop;

    /// <summary>
    /// Interface used for the indentation class to access the document.
    /// </summary>
    public interface IDocumentAccessor
    {
        #region Properties

        /// <summary>Gets if something was changed in the document.</summary>
        bool Dirty
        {
            get;
        }

        /// <summary>Gets the number of the current line.</summary>
        int LineNumber
        {
            get;
        }

        /// <summary>Gets if the current line is read only (because it is not in the
        /// selected text region)</summary>
        bool ReadOnly
        {
            get;
        }

        /// <summary>Gets/Sets the text of the current line.</summary>
        string Text
        {
            get; set;
        }

        #endregion Properties

        #region Methods

        /// <summary>Advances to the next line.</summary>
        bool Next();

        #endregion Methods
    }

    public sealed class DocumentAccessor : IDocumentAccessor
    {
        #region Fields

        int changedLines = 0;
        bool dirty;
        IDocument doc;
        LineSegment line;
        bool lineDirty = false;
        int maxLine;
        int minLine;
        int num = -1;
        string text;

        #endregion Fields

        #region Constructors

        public DocumentAccessor(IDocument document)
        {
            doc = document;
            this.minLine = 0;
            this.maxLine = doc.TotalNumberOfLines - 1;
        }

        public DocumentAccessor(IDocument document, int minLine, int maxLine)
        {
            doc = document;
            this.minLine = minLine;
            this.maxLine = maxLine;
        }

        #endregion Constructors

        #region Properties

        public int ChangedLines
        {
            get {
                return changedLines;
            }
        }

        public bool Dirty
        {
            get {
                return dirty;
            }
        }

        public int LineNumber
        {
            get {
                return num;
            }
        }

        public bool ReadOnly
        {
            get {
                return num < minLine;
            }
        }

        public string Text
        {
            get { return text; }
            set {
                if (num < minLine) return;
                text = value;
                dirty = true;
                lineDirty = true;
            }
        }

        #endregion Properties

        #region Methods

        public bool Next()
        {
            if (lineDirty) {
                doc.Replace(line.Offset, line.Length, text);
                lineDirty = false;
                ++changedLines;
            }
            ++num;
            if (num > maxLine) return false;
            line = doc.GetLineSegment(num);
            text = doc.GetText(line);
            return true;
        }

        #endregion Methods
    }

    public sealed class FileAccessor : IDisposable, IDocumentAccessor
    {
        #region Fields

        bool dirty = false;
        FileStream f;
        string filename;
        List<string> lines = new List<string>();
        int num = 0;
        StreamReader r;
        string text = "";

        #endregion Fields

        #region Constructors

        public FileAccessor(string filename)
        {
            this.filename = filename;
            f = new FileStream(filename, FileMode.Open, FileAccess.Read);
            r = ICSharpCode.TextEditor.Util.FileReader.OpenStream(f, ParserService.DefaultFileEncoding);
        }

        #endregion Constructors

        #region Properties

        public bool Dirty
        {
            get {
                return dirty;
            }
        }

        public int LineNumber
        {
            get { return num; }
        }

        public bool ReadOnly
        {
            get {
                return false;
            }
        }

        public string Text
        {
            get {
                return text;
            }
            set {
                dirty = true;
                text = value;
            }
        }

        #endregion Properties

        #region Methods

        public void Close()
        {
            System.Text.Encoding encoding = r.CurrentEncoding;
            r.Close();
            f.Close();
            if (dirty) {
                f = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
                using (StreamWriter w = new StreamWriter(f, encoding)) {
                    foreach (string line in lines) {
                        w.WriteLine(line);
                    }
                }
                f.Close();
            }
        }

        void IDisposable.Dispose()
        {
            Close();
        }

        public bool Next()
        {
            if (num > 0) {
                lines.Add(text);
            }
            text = r.ReadLine();
            ++num;
            return text != null;
        }

        #endregion Methods
    }

    public sealed class StringAccessor : IDocumentAccessor
    {
        #region Fields

        bool dirty = false;
        int num = 0;
        StringReader r;
        string text = "";
        StringWriter w;

        #endregion Fields

        #region Constructors

        public StringAccessor(string code)
        {
            r = new StringReader(code);
            w = new StringWriter();
        }

        #endregion Constructors

        #region Properties

        public string CodeOutput
        {
            get {
                return w.ToString();
            }
        }

        public bool Dirty
        {
            get {
                return dirty;
            }
        }

        public int LineNumber
        {
            get { return num; }
        }

        public bool ReadOnly
        {
            get {
                return false;
            }
        }

        public string Text
        {
            get {
                return text;
            }
            set {
                dirty = true;
                text = value;
            }
        }

        #endregion Properties

        #region Methods

        public bool Next()
        {
            if (num > 0) {
                w.WriteLine(text);
            }
            text = r.ReadLine();
            ++num;
            return text != null;
        }

        #endregion Methods
    }
}